#pragma once
#include <brpc/server.h>
#include <butil/logging.h>
#include "etcd.hpp"
#include "logger.hpp"
#include "file.pb.h"
#include "base.pb.h"
#include "tool.hpp"

// 文件存储子服务的brpc调用实现
class FileServiceImpl : public ProtoAll::FileService
{
private:
public:
    using ptr = std::shared_ptr<FileServiceImpl>;
    FileServiceImpl() {}
    ~FileServiceImpl() {}

    // 单文件下载
    void GetSingleFile(google::protobuf::RpcController *controller,
                       const ::ProtoAll::GetSingleFileReq *request,
                       ::ProtoAll::GetSingleFileRsp *response,
                       ::google::protobuf::Closure *done)
    {
        brpc::ClosureGuard rpc_guard(done);
        // 读取request中的文件名
        std::string filename = request->file_id();
        // 读取文件数据
        std::string file_date;
        bool ret = utils::read_file(filename, file_date);

        // 错误响应设置的lambda函数
        auto error_res = [this](ProtoAll::GetSingleFileRsp *response, const std::string &rid,
                                bool success, const std::string &err_message)
        {
            response->set_request_id(rid);
            response->set_success(success);
            response->set_errmsg(err_message);
        };

        
        std::string rid = request->request_id();
        if (!ret)
        {
            LOG_ERROR("文件 {} 读取失败！\n", filename);
            error_res(response, rid, false, "文件读取失败");
            return;
        }

        // 走到这里说明本次请求successful
        response->set_request_id(rid);
        response->set_success(true);
        ProtoAll::FileDownloadData *file_info = response->mutable_file_data();
        file_info->set_file_id(utils::MakeUid());
        file_info->set_file_content(file_date);
        LOG_DEBUG("文件 {} 读取成功！\n", filename);
    }

    // 多文件下载
    void GetMultiFile(google::protobuf::RpcController *controller,
                      const ::ProtoAll::GetMultiFileReq *request,
                      ::ProtoAll::GetMultiFileRsp *response,
                      ::google::protobuf::Closure *done)
    {
        brpc::ClosureGuard rpc_guard(done);
        response->set_request_id(request->request_id());
        // 遍历请求中的所有文件ID
        for (int i = 0; i < request->file_id_list_size(); ++i)
        {
            std::string fid = request->file_id_list(i);
            std::string body;
            // 读取单个文件内容
            bool ret = utils::read_file(fid, body);
            // 任何文件读取失败则整个请求失败
            if (ret == false)
            {
                response->set_success(false);
                response->set_errmsg("文件读取失败");
                return;
            }
            // 创建文件数据对象并设置内容
            ProtoAll::FileDownloadData data;
            data.set_file_id(fid);
            data.set_file_content(body);
            // 将文件数据添加到响应的文件映射中
            response->mutable_file_data()->insert({fid, data});
        }
        response->set_success(true);
        LOG_DEBUG("文件读取成功！\n");
    }

    // 单文件上传
    void PutSingleFile(google::protobuf::RpcController *controller,
                       const ::ProtoAll::PutSingleFileReq *request,
                       ::ProtoAll::PutSingleFileRsp *response,
                       ::google::protobuf::Closure *done)
    {
        brpc::ClosureGuard rpc_guard(done);
        response->set_request_id(request->request_id());
        std::string fid = utils::MakeUid();
        // 写入文件内容到存储
        bool ret = utils::write_file(fid, request->file_data().file_content());
        if (ret == false)
        {
            response->set_success(false);
            response->set_errmsg("读取文件数据失败！");
            LOG_ERROR("{} 写入文件数据失败！", request->request_id());
            return;
        }

        response->set_success(true);
        // 获取文件信息对象并设置相关信息
        response->mutable_file_info()->set_file_id(fid);
        response->mutable_file_info()->set_file_size(request->file_data().file_size());
        response->mutable_file_info()->set_file_name(request->file_data().file_name());
        LOG_DEBUG("写入文件数据成功！");
    }

    // 多文件上传
    void PutMultiFile(google::protobuf::RpcController *controller,
                      const ::ProtoAll::PutMultiFileReq *request,
                      ::ProtoAll::PutMultiFileRsp *response,
                      ::google::protobuf::Closure *done)
    {
        brpc::ClosureGuard rpc_guard(done);
        // 遍历所有上传的文件
        for (int i = 0; i < request->file_data_size(); ++i)
        {
            std::string fid = utils::MakeUid();
            bool ret = utils::write_file(fid, request->file_data(i).file_content());
            // 处理写入失败的情况 -- 任何文件失败则整个请求失败
            if (ret == false)
            {
                response->set_success(false);
                response->set_errmsg("读取文件数据失败！");
                LOG_ERROR("{} 写入文件数据失败！", request->request_id());
                return;
            }

            // 添加文件信息到响应中
            ProtoAll::FileMessageInfo *info = response->add_file_info();
            info->set_file_id(fid);
            info->set_file_size(request->file_data(i).file_size());
            info->set_file_name(request->file_data(i).file_name());
        }
        response->set_success(true);
        LOG_DEBUG("写入文件数据成功！");
    }
};

// 文件存储服务器
class FileServer
{
private:
    std::shared_ptr<brpc::Server> _server;

public:
    using ptr = std::shared_ptr<FileServer>;
    FileServer(std::shared_ptr<brpc::Server> server) : _server(server) {}

    void Start()
    {
        _server->RunUntilAskedToQuit();
    }
};

// 用于创建文件存储服务器
class FileServerBuilder
{
private:
    FileServiceImpl::ptr _file_service;
    Registry::ptr _reg_client;
    std::shared_ptr<brpc::Server> _server;

public:
    FileServerBuilder() {}
    ~FileServerBuilder() {}

    void MakeRegClientPtr(const std::string &host, const std::string &key, const std::string &val)
    {
        _reg_client = std::make_shared<Registry>(host);
        _reg_client->registry(key, val);
    }

    void MakeBrpcServerPtr(int port, int con_timeout, int thread_cnt)
    {
        _file_service = std::make_shared<FileServiceImpl>();
        _server = std::make_shared<brpc::Server>();
        int n = _server->AddService(_file_service.get(), brpc::ServiceOwnership::SERVER_DOESNT_OWN_SERVICE);
        if (n < 0)
        {
            LOG_ERROR("添加Rpc服务失败!\n");
            abort();
        }
    }

    FileServer::ptr Build()
    {
        if (!_file_service)
        {
            LOG_ERROR("rpc句柄没有初始化!\n");
            abort();
        }
        if (!_reg_client)
        {
            LOG_ERROR("服务注册中心句柄没有初始化!\n");
            abort();
        }
        auto server = std::make_shared<FileServer>(_server);
        LOG_DEBUG("SpeechServer 构造完成！\n");
        return server;
    }
};